<?php

namespace ly;

use ly\Authcode;

// 图形验证码类
class Verify {
	private $opt = [
		'w'        => 110,             // 图片宽度
		'h'        => 40,              // 图片高度
		'len'      => 4,               // 验证码字符数
		'ttf'      => __DIR__ . "/ttfs/t1.ttf", // 字体
		'expiry'   => 300,             //有效期（秒）
		'ext'      => 'gif',           // 图片格式
		'type'     => 2,               //0 数字，1 英文字母，2 数字 + 英文字母
		'fontSize' => 16,              // 验证码字体大小
		'bg'       => [243, 251, 254], //背景颜色
		'useCurve' => true,            //干扰线
		'useNoise' => true             //干扰色块
	];
	const DICTIONARY = ['1234567890', 'ABCDEFGHJKLMNPQRTUVWXY', '3456789ABCDEFGHJKLMNPQRTUVWXY'];

	private function __construct ($option = []) {
		if (is_array($option)) $this->opt = array_merge($this->opt, $option);
		if (!isset($this->opt['key']) || !is_string($this->opt['key']) || $this->opt['key'] === '')
			$this->opt['key'] = $_SERVER['REMOTE_ADDR'];
	}

	/**
	 * 生成验证码
	 *
	 * @param  array  $option  键值对数组配置
	 *
	 * @return void
	 */
	public static function create ($option = []) {
		$ins = new self($option);
		if (!isset($ins->opt['dict']) || !is_array($ins->opt['dict']) || empty($ins->opt['dict'])) {
			$dicts            = self::DICTIONARY;
			$dict             = str_split($dicts[$ins->opt['type'] < count($dicts) ? $ins->opt['type'] : 0]);
			$ins->opt['dict'] = $dict;
		}
		shuffle($ins->opt['dict']);
		$ins->opt['code'] = array_slice($ins->opt['dict'], 0, $ins->opt['len']);

		$ins->opt['w'] || $ins->opt['w'] = $ins->opt['len'] * $ins->opt['fontSize'] * 1.5 + $ins->opt['fontSize'] * 1.5;
		$ins->opt['h'] || $ins->opt['h'] = $ins->opt['fontSize'] * 2;

		ob_clean();
		@session_start();
		$_SESSION['verify'] = Authcode::encode(strtolower(join('', $ins->opt['code'])),
			$ins->opt['key'], $ins->opt['expiry']);

		$im = imagecreate($ins->opt['w'], $ins->opt['h']);
		imagecolorallocate($im, $ins->opt['bg'][0], $ins->opt['bg'][1], $ins->opt['bg'][2]);
		$_color = imagecolorallocate($im, mt_rand(1, 120), mt_rand(1, 120), mt_rand(1, 120));

		//干扰色块
		if ($ins->opt['useNoise']) $ins->_writeNoise($im);

		//干扰线
		if ($ins->opt['useCurve']) $ins->_writeCurve($im, $_color);

		// 写入验证码
		$codeNX = 0;
		foreach ($ins->opt['code'] as $d) {
			$codeNX += @mt_rand($ins->opt['fontSize'] * 1.2, $ins->opt['fontSize'] * 1.6);

			imagettftext($im, $ins->opt['fontSize'], mt_rand(-40, 70), $codeNX, $ins->opt['fontSize'] * 1.5, $_color, $ins->opt['ttf'], $d);
		}

		header('Pragma: no-cache');
		switch (strtolower($ins->opt['ext'])) {
			case 'png':
				@header("Content-Type:image/png");
				imagePNG($im);
				break;
			case 'gif':
				@header("Content-Type:image/gif");
				imageGIF($im);
				break;
			default:
				@header("Content-Type:image/jpeg");
				imageJPEG($im);
				break;
		}
		imagedestroy($im);
	}

	/**
	 * 验证验证码
	 *
	 * @param  string        $code  验证码字符串
	 * @param  string|array  $key   秘钥key或包含['key' => '***']的数组
	 *
	 * @return string|true
	 */
	public static function check ($code, $key = null) {
		$ins = is_string($key) ? new self(['key' => $key]) : new self($key);
		
		@session_start();
		if (!isset($_SESSION['verify']) || !($de = Authcode::decode($_SESSION['verify'], $ins->opt['key'])))
			return '图形验证码已过期！';

		if (strtolower($code) !== strtolower($de))
			return '图形验证码错误！';

		unset($_SESSION['verify']);

		return true;
	}

	//干扰线
	private function _writeCurve ($im, $color) {
		$A = mt_rand(1, $this->opt['h'] / 2);
		$b = mt_rand($this->opt['h'] / 4, $this->opt['h'] / 4);
		$f = mt_rand($this->opt['h'] / 4, $this->opt['h'] / 4);
		$T = mt_rand($this->opt['h'] * 1.5, $this->opt['w'] * 2);
		$w = (2 * M_PI) / $T;

		$px1 = 0;
		$px2 = @mt_rand($this->opt['w'] / 2, $this->opt['w'] * 0.667);
		for ($px = $px1; $px <= $px2; $px = $px + 0.9) {
			if (0 != $w) {
				$py = $A * sin($w * $px + $f) + $b + $this->opt['h'] / 2;
				$i  = (int)(($this->opt['fontSize'] - 6) / 4);
				while ($i > 0) {
					@imagesetpixel($im, $px + $i, $py + $i, $color);
					$i--;
				}
			}
		}

		$A   = mt_rand(1, $this->opt['h'] / 2);
		$f   = mt_rand($this->opt['h'] / 4, $this->opt['h'] / 4);
		$T   = mt_rand($this->opt['h'] * 1.5, $this->opt['w'] * 2);
		$w   = (2 * M_PI) / $T;
		$b   = $py - $A * sin($w * $px + $f) - $this->opt['h'] / 2;
		$px1 = $px2;
		$px2 = $this->opt['w'];
		for ($px = $px1; $px <= $px2; $px = $px + 0.9) {
			if (0 != $w) {
				$py = $A * sin($w * $px + $f) + $b + $this->opt['h'] / 2;
				$i  = (int)(($this->opt['fontSize'] - 8) / 4);
				while ($i > 0) {
					@imagesetpixel($im, $px + $i, $py + $i, $color);
					$i--;
				}
			}
		}
	}

	//干扰色块
	private function _writeNoise ($im) {
		for ($i = 0; $i < 10; $i++) {
			$noiseColor = imagecolorallocate($im, mt_rand(150, 225), mt_rand(150, 225), mt_rand(150, 225));
			for ($j = 0; $j < 5; $j++) {
				imagestring($im, 5, mt_rand(-10, $this->opt['w']), mt_rand(-10, $this->opt['h']), $this->opt['dict'][array_rand($this->opt['dict'])], $noiseColor);
			}
		}
	}
}